package org.xebia.roo.addon.compareto;

import java.util.ArrayList;
import java.util.List;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.TypeManagementService;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.MemberFindingUtils;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.Repository;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Element;

/**
 * Implementation of operations this add-on offers.
 * 
 * @since 1.1
 */
@Component
// Use these Apache Felix annotations to register your commands class in the Roo
// container
@Service
public class ComparetoOperationsImpl implements ComparetoOperations {

    /**
     * Use ProjectOperations to install new dependencies, plugins, properties,
     * etc into the project configuration
     */
    @Reference
    private ProjectOperations projectOperations;

    /**
     * Use TypeLocationService to find types which are annotated with a given
     * annotation in the project
     */
    @Reference
    private TypeLocationService typeLocationService;

    /**
     * Use TypeManagementService to change types
     */
    @Reference
    private TypeManagementService typeManagementService;

    /** {@inheritDoc} */
    public boolean isCommandAvailable() {
        // Check if a project has been created
        return projectOperations.isFocusedProjectAvailable();
    }

    /** {@inheritDoc} */
    public void addCompareToMethod(JavaType javaType) {
        Assert.notNull(javaType, "Java type required");

        ClassOrInterfaceTypeDetails existing = typeLocationService.getTypeDetails(javaType);

        if (existing != null && MemberFindingUtils.getAnnotationOfType(existing.getAnnotations(), new JavaType(RooCompareTo.class.getName())) == null) {
            ClassOrInterfaceTypeDetailsBuilder classOrInterfaceTypeDetailsBuilder = new ClassOrInterfaceTypeDetailsBuilder(existing);

            JavaType rooRooCompareto = new JavaType(RooCompareTo.class.getName());

            AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(rooRooCompareto);

            classOrInterfaceTypeDetailsBuilder.addAnnotation(annotationBuilder.build());

            typeManagementService.createOrUpdateTypeOnDisk(classOrInterfaceTypeDetailsBuilder.build());
        }
    }

    /** {@inheritDoc} */
    public void addCompareToMethodToAllJavaBeans() {
        for (JavaType type : typeLocationService.findTypesWithAnnotation(new JavaType("org.springframework.roo.addon.javabean.RooJavaBean"))) {
            addCompareToMethod(type);
        }
    }

    /** {@inheritDoc} */
    public void setup() {
        projectOperations.addRepository("", new Repository("Compareto Roo add-on repository", "Compareto Roo add-on repository",
                "https://spring-roo-compareto-addon.googlecode.com/svn/repo"));

        List<Dependency> dependencies = new ArrayList<Dependency>();

        Element configuration = XmlUtils.getConfiguration(getClass());
        for (Element dependencyElement : XmlUtils.findElements("/configuration/dependencies/dependency", configuration)) {
            dependencies.add(new Dependency(dependencyElement));
        }

        projectOperations.addDependencies("", dependencies);

        List<Element> repositories = XmlUtils.findElements(
                "/configuration/repositories/repository", configuration);
        for (Element repositoryElement : repositories) {
            Repository repository = new Repository(repositoryElement);
            projectOperations.addRepository(projectOperations.getFocusedModuleName(), repository);
        }
    }
}